package com.metaera.app.data;

import android.content.Context;
import android.content.SharedPreferences;

import com.google.gson.Gson;
import com.metaera.app.MetaEraApplication;
import com.metaera.app.data.model.ActivityListResponse;
import com.metaera.app.data.model.AppConfigResponse;
import com.metaera.app.data.model.ArticleListResponse;
import com.metaera.app.data.model.BasicListResponse;
import com.metaera.app.data.model.BasicResponse;
import com.metaera.app.data.model.CollectionListResponse;
import com.metaera.app.data.model.FeedbackResponse;
import com.metaera.app.data.model.FollowAuthorResponse;
import com.metaera.app.data.model.HistoryListResponse;
import com.metaera.app.data.model.HomeNewsTypeResponse;
import com.metaera.app.data.model.HomeTopNewsResponse;
import com.metaera.app.data.model.HotColumnResponse;
import com.metaera.app.data.model.HotSearchResponse;
import com.metaera.app.data.model.InformationDetailsResponse;
import com.metaera.app.data.model.InvestFinanceResponse;
import com.metaera.app.data.model.MorningPostResponse;
import com.metaera.app.data.model.NoticeListResponse;
import com.metaera.app.data.model.PhoneCodeResponse;
import com.metaera.app.data.model.ProfileResponse;
import com.metaera.app.data.model.SearchResponse;
import com.metaera.app.data.model.SearchResultResponse;
import com.metaera.app.data.model.SpecialDetailsResponse;
import com.metaera.app.data.model.SpecialResponse;
import com.metaera.app.data.model.TodayEventResponse;
import com.metaera.app.data.model.UploadImageResponse;
import com.metaera.app.data.model.UserDetailsResponse;
import com.metaera.app.data.model.UserNoticeResponse;
import com.metaera.app.data.model.VersionUpInfoResponse;

import java.io.File;
import java.util.List;
import java.util.Locale;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.schedulers.Schedulers;


/**
 * 数据提供
 */
public class DataRepository implements IDataSource {

    private static IDataSource sDataRepository;

    public static IDataSource getInstance() {
        if (sDataRepository == null) {
            synchronized (DataRepository.class) {

                if (sDataRepository == null) {
                    sDataRepository = new DataRepository();
                }
            }
        }

        return sDataRepository;
    }

    private       SharedPreferences mPreferences;
    private final IDataSource       mRemoteDataSource, mLocalCacheDataSource;


    private DataRepository() {
        mRemoteDataSource = new RemoteDataSource();
        mLocalCacheDataSource = new LocalCacheDataSource();

        // 本地存储
        mPreferences = MetaEraApplication.sInstance
                .getSharedPreferences("data_local_cache", Context.MODE_PRIVATE);

        getAppConfig().subscribeOn(Schedulers.io()).subscribe(); // app信息
    }

    private <T> void pullData(
            String key,
            ObservableEmitter<BasicResponse<T>> emitter,
            Observable<BasicResponse<T>> local,
            Observable<BasicResponse<T>> remote
    ) {

        // 1. 获取本地数据
        BasicResponse<T> response = local.doOnError(emitter::onError).blockingFirst();

        // 2. 本地数据则返回本地，同时获取远程数据
        if (response != null && response.isSucceed()) {
            emitter.onNext(response);

            try {

                response = remote.blockingFirst();
                if (response != null) {
                    emitter.onNext(response);
                    // 3. 将最新远程数据存储至本地，同时刷新
                    mPreferences.edit().putString(key, new Gson().toJson(response)).apply();
                }

            } catch (Exception e) {
                e.printStackTrace();
            }

        } else {

            response = remote.doOnError(emitter::onError).blockingFirst();
            if (response != null) {
                emitter.onNext(response);
                // 3. 将最新远程数据存储至本地，同时刷新
                mPreferences.edit().putString(key, new Gson().toJson(response)).apply();
            }

        }

        emitter.onComplete();
    }

    private <T> void pullListData(
            String key,
            ObservableEmitter<BasicListResponse<T>> emitter,
            Observable<BasicListResponse<T>> local,
            Observable<BasicListResponse<T>> remote
    ) {

        // 1. 获取本地数据
        BasicListResponse<T> response = local.doOnError(emitter::onError).blockingFirst();

        // 2. 本地数据则返回本地，同时获取远程数据
        if (response != null && response.isSucceed()) {
            emitter.onNext(response);

            try {

                response = remote.blockingFirst();
                if (response != null) {
                    emitter.onNext(response);
                    // 3. 将最新远程数据存储至本地，同时刷新

                    mPreferences.edit().putString(key, new Gson().toJson(response)).apply();
                }

            } catch (Exception e) {
                e.printStackTrace();
            }

        } else {

            response = remote.doOnError(emitter::onError).blockingFirst();
            if (response != null) {
                emitter.onNext(response);
                // 3. 将最新远程数据存储至本地，同时刷新
                mPreferences.edit().putString(key, new Gson().toJson(response)).apply();
            }

        }

        emitter.onComplete();
    }


    @Override
    public Observable<BasicResponse<UploadImageResponse>> upload(File file) {
        return mRemoteDataSource.upload(file);
    }

    @Override
    public Observable<BasicResponse<List<HotSearchResponse>>> hotSearch() {
        return Observable.create(emitter ->
                pullData(
                        LocalCacheDataSource.hotSearch,
                        emitter,
                        mLocalCacheDataSource.hotSearch(),
                        mRemoteDataSource.hotSearch()
                )
        );

    }

    @Override
    public Observable<BasicResponse<HomeTopNewsResponse>> homeTopNews() {
        return Observable.create(emitter ->
                pullData(
                        LocalCacheDataSource.homeTopNews,
                        emitter,
                        mLocalCacheDataSource.homeTopNews(),
                        mRemoteDataSource.homeTopNews()
                )
        );
    }

    @Override
    public Observable<BasicListResponse<MorningPostResponse>> getNewsDetails(int page, int size) {
        return mRemoteDataSource.getNewsDetails(page, size);
    }

    @Override
    public Observable<BasicListResponse<InvestFinanceResponse>> getInvestDetails(int page, int size) {
        return Observable.create(emitter ->
                pullListData(
                        String.format(Locale.getDefault(), LocalCacheDataSource.getInvestDetails, page, size),
                        emitter,
                        mLocalCacheDataSource.getInvestDetails(page, size),
                        mRemoteDataSource.getInvestDetails(page, size)
                )
        );

//        return mRemoteDataSource.getInvestDetails(page, size);
    }

    @Override
    public Observable<BasicListResponse<TodayEventResponse>> getEventDetails(int page, int size) {
//        return mRemoteDataSource.getEventDetails(page, size);
        return Observable.create(emitter ->
                pullListData(
                        String.format(Locale.getDefault(), LocalCacheDataSource.getEventDetails, page, size),
                        emitter,
                        mLocalCacheDataSource.getEventDetails(page, size),
                        mRemoteDataSource.getEventDetails(page, size)
                )
        );
    }

    @Override
    public Observable<BasicResponse<List<HomeNewsTypeResponse>>> categoryLists(int type) {
//        return mRemoteDataSource.categoryLists(type);
        return Observable.create(emitter ->
                pullData(
                        String.format(Locale.getDefault(), LocalCacheDataSource.categoryLists_key, type),
                        emitter,
                        mLocalCacheDataSource.categoryLists(type),
                        mRemoteDataSource.categoryLists(type)
                )
        );
    }

    @Override
    public Observable<BasicListResponse<ArticleListResponse>> getArticleList(
            int category_id, int type, int page, int size) {

        if (page <= 1) {

            return Observable.create(emitter ->
                    pullListData(
                            String.format(Locale.getDefault(), LocalCacheDataSource.getArticleList, category_id, type, page, size),
                            emitter,
                            mLocalCacheDataSource.getArticleList(category_id, type, page, size),
                            mRemoteDataSource.getArticleList(category_id, type, page, size)
                    )
            );
        }

        return mRemoteDataSource.getArticleList(category_id, type, page, size);
    }

    @Override
    public Observable<BasicResponse<UserDetailsResponse>> getUserInfo() {
        return mRemoteDataSource.getUserInfo();
    }

    @Override
    public Observable<BasicResponse<ProfileResponse>> toUserInfo(int id) {
        return mRemoteDataSource.toUserInfo(id);
    }

    @Override
    public Observable<BasicResponse<Object>> editUserInfo(
            String nickname, String avatar_img, String sex, String position, String desc,
            String weibo, String official_account, String twitter, String email,
            String is_show_phone, String phone, String knowledge_planet,
            String is_show_wechat, String wechat) {
        return mRemoteDataSource.editUserInfo(nickname, avatar_img, sex, position, desc, weibo,
                official_account, twitter, email, is_show_phone, phone, knowledge_planet
                , is_show_wechat, wechat);
    }


    @Override
    public Observable<BasicResponse<UserDetailsResponse>> login(
            int type, String code, String mobile, String password, String phone_code) {
//        return Observable.create(emitter -> pullData(
//                        String.format("user(%s)", mobile),
//                        emitter,
//                        mLocalCacheDataSource.login(type, code, mobile, password,  phone_code),
//                        mRemoteDataSource.login(type, code, mobile, password,  phone_code)
//                )
//        );

        return mRemoteDataSource.login(type, code, mobile, password, phone_code);
    }

    @Override
    public Observable<BasicResponse<List<PhoneCodeResponse>>> getPhoneCode() {
//        return mRemoteDataSource.getPhoneCode();
        return Observable.create(emitter ->
                pullData(
                        String.format(Locale.getDefault(), LocalCacheDataSource.getPhoneCode),
                        emitter,
                        mLocalCacheDataSource.getPhoneCode(),
                        mRemoteDataSource.getPhoneCode()
                )
        );
    }

    @Override
    public Observable<BasicResponse<Object>> sendSms(String phone_code, String mobile) {
        return mRemoteDataSource.sendSms(phone_code, mobile);
    }

    @Override
    public Observable<BasicResponse<Object>> setPassword(String code, String mobile, String phone_code, String password1, String password2) {
        return mRemoteDataSource.setPassword(code, mobile, phone_code, password1, password2);
    }

    @Override
    public Observable<BasicResponse<Object>> forgotPassword(String code, String mobile, String phone_code, String password1, String password2) {
        return mRemoteDataSource.forgotPassword(code, mobile, phone_code, password1, password2);
    }

    @Override
    public Observable<BasicListResponse<HistoryListResponse>> getHistoryList(int page, int size) {
        return mRemoteDataSource.getHistoryList(page, size);
    }

    @Override
    public Observable<BasicListResponse<CollectionListResponse>> myCollection(int page, int size) {
        return mRemoteDataSource.myCollection(page, size);
    }

    @Override
    public Observable<BasicListResponse<NoticeListResponse>> getNoticeList(int status, int page, int size) {
        return Observable.create(emitter ->
                pullListData(
                        String.format(Locale.getDefault(), LocalCacheDataSource.getNoticeList, status, page, size),
                        emitter,
                        mLocalCacheDataSource.getNoticeList(status, page, size),
                        mRemoteDataSource.getNoticeList(status, page, size)
                )
        );

//        return mRemoteDataSource.getNoticeList(status, page, size);
    }

    @Override
    public Observable<BasicResponse<List<FeedbackResponse>>> getFeedbackLog() {
        return mRemoteDataSource.getFeedbackLog();
    }

    @Override
    public Observable<BasicResponse<Object>> submitFeedback(int type, String content, String name, String contact) {
        return mRemoteDataSource.submitFeedback(type, content, name, contact);
    }

    @Override
    public Observable<BasicResponse<Object>> cleanHistory() {

        return mRemoteDataSource.cleanHistory();
    }

    @Override
    public Observable<BasicResponse<List<ArticleListResponse>>> relatedRecommend(String id) {
//        return mRemoteDataSource.relatedRecommend(id);
        return Observable.create(emitter ->
                pullData(
                        // 此处不按接口存储
                        String.format(Locale.getDefault(), LocalCacheDataSource.relatedRecommend, 0),
                        emitter,
                        mLocalCacheDataSource.relatedRecommend(id),
                        mRemoteDataSource.relatedRecommend(id)
                )
        );
    }

    @Override
    public Observable<BasicResponse<InformationDetailsResponse>> getInformationDetails(String id) {
//        return mRemoteDataSource.getInformationDetails(id);
        return Observable.create(emitter ->
                pullData(
                        String.format(Locale.getDefault(), LocalCacheDataSource.getInformationDetails, id),
                        emitter,
                        mLocalCacheDataSource.getInformationDetails(id),
                        mRemoteDataSource.getInformationDetails(id)
                )
        );
    }

    @Override
    public Observable<BasicListResponse<HotColumnResponse>> getPopularColumn(int type, int page, int size) {
//        return mRemoteDataSource.getPopularColumn(type, page, size);
        return Observable.create(emitter ->
                pullListData(
                        String.format(Locale.getDefault(), LocalCacheDataSource.getPopularColumn, type, page, size),
                        emitter,
                        mLocalCacheDataSource.getPopularColumn(type, page, size),
                        mRemoteDataSource.getPopularColumn(type, page, size)
                )
        );
    }

    @Override
    public Observable<BasicResponse<SpecialResponse>> getSpecial(int page, int size) {
//        return mRemoteDataSource.getSpecial(page, size);
        return Observable.create(emitter ->
                pullData(
                        String.format(Locale.getDefault(), LocalCacheDataSource.getSpecial, page, size),
                        emitter,
                        mLocalCacheDataSource.getSpecial(page, size),
                        mRemoteDataSource.getSpecial(page, size)
                )
        );
    }

    @Override
    public Observable<BasicListResponse<ArticleListResponse>> appointDateCalendar(
            String date, int categoryID) {
//        return mRemoteDataSource.appointDateCalendar(date, categoryID);
        return Observable.create(emitter ->
                pullListData(
                        String.format(Locale.getDefault(), LocalCacheDataSource.appointDateCalendar, date, categoryID),
                        emitter,
                        mLocalCacheDataSource.appointDateCalendar(date, categoryID),
                        mRemoteDataSource.appointDateCalendar(date, categoryID)
                )
        );
    }

    @Override
    public Observable<BasicResponse<List<String>>> calendarList(String date, int categoryID) {
//        return mRemoteDataSource.calendarList(date, categoryID);
        return Observable.create(emitter ->
                pullData(
                        String.format(Locale.getDefault(), LocalCacheDataSource.calendarList, date, categoryID),
                        emitter,
                        mLocalCacheDataSource.calendarList(date, categoryID),
                        mRemoteDataSource.calendarList(date, categoryID)
                )
        );
    }

    @Override
    public Observable<BasicListResponse<ArticleListResponse>> getUserInformation(String id, int page, int size) {
        return mRemoteDataSource.getUserInformation(id, page, size);
    }

    @Override
    public Observable<BasicListResponse<ActivityListResponse>> getActivityList(int type, int page, int size) {
//        return mRemoteDataSource.getActivityList(type, page, size);
        return Observable.create(emitter ->
                pullListData(
                        String.format(Locale.getDefault(), LocalCacheDataSource.getActivityList, type, page, size),
                        emitter,
                        mLocalCacheDataSource.getActivityList(type, page, size),
                        mRemoteDataSource.getActivityList(type, page, size)
                )
        );
    }

    @Override
    public Observable<BasicListResponse<ArticleListResponse>> getFollowLists(int page, int size) {
//        return mRemoteDataSource.getFollowLists(page, size);
        return Observable.create(emitter ->
                pullListData(
                        String.format(Locale.getDefault(), LocalCacheDataSource.getFollowLists, page, size),
                        emitter,
                        mLocalCacheDataSource.getFollowLists(page, size),
                        mRemoteDataSource.getFollowLists(page, size)
                )
        );
    }

    @Override
    public Observable<BasicResponse<ActivityListResponse>> getActivityDetails(int id) {
        return mRemoteDataSource.getActivityDetails(id);
    }

    @Override
    public Observable<BasicResponse<AppConfigResponse>> getAppConfig() {

        return Observable.create(emitter ->
                pullData(
                        String.format(Locale.getDefault(), LocalCacheDataSource.getAppConfig),
                        emitter,
                        mLocalCacheDataSource.getAppConfig(),
                        mRemoteDataSource.getAppConfig()
                )
        );

//        return mRemoteDataSource.getAppConfig();
    }

    @Override
    public Observable<BasicListResponse<SearchResultResponse>> searchMore(
            String key, int orderType, int timeType, int type, int page, int size) {
        return mRemoteDataSource.searchMore(key, orderType, timeType, type, page, size);
    }

    @Override
    public Observable<BasicResponse<SearchResponse>> search(String key, int orderType, int timeType) {
//        return mRemoteDataSource.search(key, orderType, timeType);
        return Observable.create(emitter ->
                pullData(
                        String.format(Locale.getDefault(), LocalCacheDataSource.search, key, orderType, timeType),
                        emitter,
                        mLocalCacheDataSource.search(key, orderType, timeType),
                        mRemoteDataSource.search(key, orderType, timeType)
                )
        );
    }

    @Override
    public Observable<BasicListResponse<ArticleListResponse>> getRecommendLists(int page, int size) {
//        return mRemoteDataSource.getRecommendLists(page, size);
        return Observable.create(emitter ->
                pullListData(
                        String.format(Locale.getDefault(), LocalCacheDataSource.getRecommendLists, page, size),
                        emitter,
                        mLocalCacheDataSource.getRecommendLists(page, size),
                        mRemoteDataSource.getRecommendLists(page, size)
                )
        );
    }

    @Override
    public Observable<BasicListResponse<ArticleListResponse>> getLabelDetails(int id, int page, int size) {
        return mRemoteDataSource.getLabelDetails(id, page, size);
    }

    @Override
    public Observable<BasicResponse<Object>> followAuthor(int author_id, int is_status) {
        return mRemoteDataSource.followAuthor(author_id, is_status);
    }

    @Override
    public Observable<BasicResponse<VersionUpInfoResponse>> versionUpInfo() {
        return mRemoteDataSource.versionUpInfo();
    }

    @Override
    public Observable<BasicResponse<Object>> likeInformation(int id, int is_status, int type) {
        return mRemoteDataSource.likeInformation(id, is_status, type);
    }

    @Override
    public Observable<BasicResponse<SpecialDetailsResponse>> getSpecialDetails(int id) {
//        return mRemoteDataSource.getSpecialDetails(id);
        return Observable.create(emitter ->
                pullData(
                        String.format(Locale.getDefault(), LocalCacheDataSource.getSpecialDetails, id),
                        emitter,
                        mLocalCacheDataSource.getSpecialDetails(id),
                        mRemoteDataSource.getSpecialDetails(id)
                )
        );
    }

    @Override
    public Observable<BasicResponse<UserDetailsResponse>> weChatLogin(String code) {
        return mRemoteDataSource.weChatLogin(code);
    }

    @Override
    public Observable<BasicResponse<UserDetailsResponse>> bindMobile(String code, String mobile, String phone_code, String token) {
        return mRemoteDataSource.bindMobile(code, mobile, phone_code, token);
    }

    @Override
    public Observable<BasicResponse<Object>> collectionInformation(int id) {
        return mRemoteDataSource.collectionInformation(id);
    }

    @Override
    public Observable<BasicResponse<Object>> cancelCollectionInformation(int id) {
        return mRemoteDataSource.cancelCollectionInformation(id);
    }

    @Override
    public Observable<BasicListResponse<FollowAuthorResponse>> getFollowList(int page, int size) {
//        return mRemoteDataSource.getFollowList(page, size);
        return Observable.create(emitter ->
                pullListData(
                        String.format(Locale.getDefault(), LocalCacheDataSource.getFollowList, page, size),
                        emitter,
                        mLocalCacheDataSource.getFollowList(page, size),
                        mRemoteDataSource.getFollowList(page, size)
                )
        );
    }

    @Override
    public Observable<BasicResponse<Object>> updateAuthorNotice(int id, int is_notice) {
        return mRemoteDataSource.updateAuthorNotice(id, is_notice);
    }

    @Override
    public Observable<BasicResponse<UserNoticeResponse>> getUserNotice() {
        return mRemoteDataSource.getUserNotice();
    }

    @Override
    public Observable<BasicResponse<Object>> updateUserNotice(
            int id,String is_new_notice, String is_disturb, String disturb_start_time, String disturb_end_time,
            String is_article, String is_news_flash, String is_notice, String is_author_update) {

        return mRemoteDataSource.updateUserNotice(id, is_new_notice, is_disturb, disturb_start_time,
                disturb_end_time, is_article, is_news_flash, is_notice, is_author_update);
    }

    @Override
    public Observable<BasicResponse<Object>> logOff() {
        return mRemoteDataSource.logOff();
    }


}
